// Package chat
// @Author: zhangdi
// @File: gpt_local
// @Version: 1.0.0
// @Date: 2023/6/9 15:07
package chat

import (
	"context"
	"errors"
	"fmt"
	openai "github.com/sashabaranov/go-openai"
	"ideatools/application/constant/enums"
	"ideatools/application/pkgs/mylog"
	"io"
)

type GptLocal struct {
	client *openai.Client
	conf   *GptConfig
}

// InitGpt 初始化GPT key:open key
func (g *GptLocal) InitGpt(config *GptConfig) error {
	g.conf = config
	g.NewClient(config)
	return nil
}

// UpdateConfig 检测配置，如果不一样则更新client
func (g *GptLocal) UpdateConfig(newConfig *GptConfig) error {

	return g.CheckReLoadClient(newConfig)
}

func (g *GptLocal) CheckReLoadClient(config *GptConfig) error {
	if config.ProxyType != g.conf.ProxyType {
		g.NewClient(config)
	}
	if config.OpenAiKey != g.conf.OpenAiKey {
		g.NewClient(config)
	}
	if config.ProxyServerUrl != g.conf.ProxyServerUrl {
		g.NewClient(config)
	}
	if config.ProxyLocalUrl != g.conf.ProxyLocalUrl {
		g.NewClient(config)
	}
	g.conf = config
	return nil
}

func (g *GptLocal) NewClient(config *GptConfig) {

	mylog.Debug(fmt.Sprintf("type:%v,local:%s,server:%s", config.ProxyType, config.ProxyLocalUrl, config.ProxyServerUrl))
	ClientConfig := openai.DefaultConfig(config.OpenAiKey)
	if !g.ProxyTypeIsLocal() {
		//远程地址格式例如 ："http://xxxxx.com/v1"
		//mylog.Debug("使用远程地址" + config.ProxyServerUrl)
		ClientConfig.BaseURL = config.ProxyServerUrl
	}

	g.client = openai.NewClientWithConfig(ClientConfig)
}

// GetProxyServeUrl 获取远程代理地址，如果没有则返回 ""
func (g *GptLocal) GetProxyServeUrl() string {
	return g.conf.ProxyServerUrl
}

// GetProxyLocalUrl 获取本机代理设置
func (g *GptLocal) GetProxyLocalUrl() string {
	if g.conf.ProxyLocalUrl == "" {
		//例如 ："http://xxxxx.com/v1"
		return "http://127.0.0.1:10809"
	}
	return g.conf.ProxyLocalUrl
}

func (g *GptLocal) ProxyTypeIsLocal() bool {
	if g.conf.ProxyType == "" {
		return true
	}
	if g.GetProxyServeUrl() == "" {
		return true
	}

	if g.conf.ProxyType == enums.ProxyTypeLocal {
		return true
	}

	return false
}

// SendContents 获取文本
func (g *GptLocal) SendContents(contents []CompletionMessage) (string, error) {
	if g.ProxyTypeIsLocal() {
		mylog.Debug("使用本地代理")
		SetLocalProxy(g.GetProxyLocalUrl())
	} else {
		mylog.Debug("使用远程代理")
	}
	gptModel := g.conf.GptModel
	if gptModel == "" {
		gptModel = enums.GPTModelChat3Dot5Turbo0613
	}

	if g.conf.IsStream && g.conf.StreamHandle != nil {
		g.CompleteChatStream(gptModel, contents)
		return "", nil
	} else {
		// /v1/chat/completions接口
		return g.CompleteChat(gptModel, contents)
	}

}
func (g *GptLocal) CompleteChatStream(modelName string, SendContents []CompletionMessage) {
	req := openai.ChatCompletionRequest{
		Model: modelName,
		//MaxTokens: 500,
		Messages: SendContents,
		Stream:   true,
	}
	ctx := context.Background()
	stream, err := g.client.CreateChatCompletionStream(ctx, req)
	if err != nil {
		g.conf.StreamHandle(err.Error(), true, true)
		return
	}

	defer stream.Close()

	for {
		response, err := stream.Recv()
		if errors.Is(err, io.EOF) {
			//正常结束
			g.conf.StreamHandle("\n", true, false)
			g.cancelProxy()
			return
		}

		if err != nil { //异常结束
			g.conf.StreamHandle("Stream error:"+err.Error(), true, true)
			g.cancelProxy()
			return
		}
		if response.Choices != nil && len(response.Choices) > 0 {
			g.conf.StreamHandle(response.Choices[0].Delta.Content, false, false)
		} else {
			fmt.Printf("response.Choices[0] empty")
		}
	}
}
func (g *GptLocal) CompleteChat(modelName string, SendContents []CompletionMessage) (string, error) {
	resp, err := g.client.CreateChatCompletion(
		context.Background(),
		openai.ChatCompletionRequest{
			Model:    modelName,
			Messages: SendContents,
		},
	)
	if err != nil {
		mylog.Debug("发送error:" + err.Error())
		return "", err
	}
	if g.ProxyTypeIsLocal() {
		ResetLocalProxy() //完成后恢复代理 不然wails没法正常运行
	}

	if len(resp.Choices) > 0 {
		return resp.Choices[0].Message.Content, err
	} else {
		return "", err
	}
}

func (g *GptLocal) cancelProxy() {
	if g.ProxyTypeIsLocal() {
		SetLocalProxy(g.GetProxyLocalUrl())
	}
}

func (g *GptLocal) GetCurrKey() string {
	return g.conf.OpenAiKey
}
